package com.zff.rental.service.apartment;


import com.zff.rental.common.BusinessException;
import com.zff.rental.common.PageVo;
import com.zff.rental.common.ResultCode;
import com.zff.rental.entity.apartment.*;
import com.zff.rental.entity.user.UserEntity;
import com.zff.rental.mapstruct.apartment.AttachmentMapstruct;
import com.zff.rental.mapstruct.apartment.DeviceMapstruct;
import com.zff.rental.mapstruct.apartment.RoomMapstruct;
import com.zff.rental.mapstruct.user.UserMapstruct;
import com.zff.rental.service.BaseService;
import com.zff.rental.utils.ObjUtils;
import com.zff.rental.utils.collectors.CollectionsUtils;
import com.zff.rental.utils.specification.SpecBuilder;
import com.zff.rental.vo.apartment.attachment.EditAttachmentVo;
import com.zff.rental.vo.apartment.room.*;
import com.zff.rental.vo.user.UserVo;
import jakarta.annotation.Resource;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author HCY
 */
@Service
public class RoomServer extends BaseService {
    @Resource
    private ApartmentPermissionServer apartmentPermissionServer;

    @Transactional
    public RoomVo add(AddRoomParams params) {
        RoomEntity map = RoomMapstruct.INSTANCE.map(params);

        if (roomRepository.existsByApartmentIdAndName(params.getApartmentId(), params.getName())) {
            throw new BusinessException("该公寓下已存在该房间名称");
        }

        RoomEntity saveResult = roomRepository.save(map);

        RoomVo vo = RoomMapstruct.INSTANCE.map(saveResult);

        // 保存附件
        Long id = saveResult.getId();
        List<AttachmentEntity> attachmentEntityList = AttachmentMapstruct.INSTANCE.map(params.getAttachmentList(), id, 2);
        if (!attachmentEntityList.isEmpty()) {
            List<AttachmentEntity> saveAttachmentList = attachmentRepository.saveAll(attachmentEntityList);
            vo.setAttachmentList(AttachmentMapstruct.INSTANCE.map(saveAttachmentList));
        }

        // 添加设备
        List<Long> deviceIdList = params.getDeviceIdList();
        if (deviceIdList != null && !deviceIdList.isEmpty()) {
            List<TrRoomDeviceEntity> deviceList = deviceIdList.stream()
                    .map(deviceId -> new TrRoomDeviceEntity(id, deviceId))
                    .collect(Collectors.toList());
            List<TrRoomDeviceEntity> saveDeviceList = trRoomDeviceRepository.saveAll(deviceList);

            List<DeviceDictEntity> deviceDictList = deviceDictRepository.findAllById(
                    saveDeviceList.stream().map(TrRoomDeviceEntity::getDeviceId).collect(Collectors.toList()));

            vo.setDeviceList(DeviceMapstruct.INSTANCE.map(deviceDictList));
        }

        return vo;
    }

    @Transactional
    public Boolean batchAdd(BatchAddRoomParams params) {
        Long apartmentId = params.getApartmentId();
        List<BatchAddRoomParams.Room> roomList = params.getRoomList();
        List<String> roomNameList = roomList.stream().map(BatchAddRoomParams.Room::getName).toList();

        // 已经存在的房间名称
        Set<String> olsRoomNameSet = roomRepository.findAll(SpecBuilder.<RoomEntity>builder()
                        .equal("apartmentId", params.getApartmentId())
                        .in("name", roomNameList)
                        .buildAnd())
                .stream()
                .map(RoomEntity::getName).collect(Collectors.toSet());

        // 需要添加的房间
        List<RoomEntity> needSaveRoomList = roomList
                .stream()
                .filter(room -> !olsRoomNameSet.contains(room.getName()))
                .map(room -> {
                    RoomEntity item = new RoomEntity();
                    item.setApartmentId(apartmentId);
                    item.setName(room.getName());
                    item.setFloor(room.getFloor());
                    return item;
                })
                .toList();

        if (needSaveRoomList.isEmpty()) {
            return true;
        }
        roomRepository.saveAll(needSaveRoomList);
        return true;
    }

    @Transactional
    public RoomVo modify(ModifyRoomParams params) {
        Long roomId = params.getId();
        RoomEntity findResult = roomRepository.findById(roomId).orElseThrow(ResultCode.RECORD_NOT_EXISTS::toException);
        if (findResult == null) {
            throw ResultCode.RECORD_NOT_EXISTS.toException();
        }
        RoomMapstruct.INSTANCE.merge(findResult, params);
        RoomEntity saveResult = roomRepository.save(findResult);

        RoomVo vo = RoomMapstruct.INSTANCE.map(saveResult);

        // 修改附件
        List<AttachmentEntity> saveAttachmentList = attachmentRepository.saveAllOrDelete(params.getAttachmentList(), roomId, 2);
        vo.setAttachmentList(AttachmentMapstruct.INSTANCE.map(saveAttachmentList));

        // 修改设备
        List<Long> deviceIdList = params.getDeviceIdList();
        List<TrRoomDeviceEntity> deviceList = trRoomDeviceRepository.saveAllOrDelete(roomId, deviceIdList);
        if (deviceList != null && !deviceList.isEmpty()) {
            List<DeviceDictEntity> deviceDictList = deviceDictRepository.findAllById(
                    deviceList.stream().map(TrRoomDeviceEntity::getDeviceId).collect(Collectors.toList()));
            vo.setDeviceList(DeviceMapstruct.INSTANCE.map(deviceDictList));
        }
        return vo;
    }

    @Transactional
    public boolean batchModify(BatchModifyRoomParams params) {
        List<Long> roomIdList = params.getIdList();
        List<RoomEntity> roomList = roomRepository.findAllById(roomIdList);
        if (roomList.isEmpty()) {
            return false;
        }
        RoomMapstruct.INSTANCE.mergeList(roomList, params);
        roomRepository.saveAll(roomList);

        // 修改附件
        List<EditAttachmentVo> srcVoList = params.getAttachmentList();
        if (srcVoList != null && !srcVoList.isEmpty()) {
            List<AttachmentEntity> newList = new ArrayList<>();
            for (RoomEntity roomEntity : roomList) {
                for (EditAttachmentVo editAttachmentVo : srcVoList) {
                    newList.add(AttachmentMapstruct.INSTANCE.map(editAttachmentVo, roomEntity.getId(), 2));
                }
            }

            // 附件不为空，则替换原来的附件（删除多余的，更新重合的，添加新增的）
            List<AttachmentEntity> oldList = attachmentRepository.findAllByBelongIdInAndBelongType(roomIdList, 2);
            if (!newList.isEmpty()) {
                // 以房间Id+objectKey为key，
                Map<String, AttachmentEntity> oldMap = oldList.stream().collect(Collectors.toMap(a -> a.getBelongId() + "," + a.getObjectKey(), e -> e, (v1, v2) -> v1));

                // 补全newList的Id（更新这部分数据，Id为空是插入）
                for (AttachmentEntity attachmentEntity : newList) {
                    AttachmentEntity old = oldMap.get(attachmentEntity.getBelongId() + "," + attachmentEntity.getObjectKey());
                    if (old != null) {
                        attachmentEntity.setId(old.getId());
                    }
                }
                Set<Long> updateIdSet = newList.stream().map(AttachmentEntity::getId).filter(Objects::nonNull).collect(Collectors.toSet());
                Set<Long> deletedIdSet = oldList.stream().map(AttachmentEntity::getId).filter(id -> !updateIdSet.contains(id)).collect(Collectors.toSet());
                if (!deletedIdSet.isEmpty()) {
                    attachmentRepository.deleteAllByIdInBatch(deletedIdSet);
                }
            }
            // 更新和新增
            attachmentRepository.saveAll(newList);
        }

        // 修改设备（删除多余的，添加新增的）
        List<Long> deviceIdList = params.getDeviceIdList();
        if (deviceIdList != null && !deviceIdList.isEmpty()) {
            List<TrRoomDeviceEntity> srcList = new ArrayList<>();
            for (RoomEntity roomEntity : roomList) {
                for (Long deviceId : deviceIdList) {
                    srcList.add(new TrRoomDeviceEntity(roomEntity.getId(), deviceId));
                }
            }
            List<TrRoomDeviceEntity> oldList = trRoomDeviceRepository.findAllByRoomIdIn(roomIdList);
            Set<TrRoomDeviceEntity> addList = CollectionsUtils.reduceToSet(srcList, oldList);
            Set<TrRoomDeviceEntity> delList = CollectionsUtils.reduceToSet(oldList, srcList);

            if (!addList.isEmpty()) {
                trRoomDeviceRepository.saveAll(addList);
            }
            if (!delList.isEmpty()) {
                trRoomDeviceRepository.deleteAll(delList);
            }
        }
        return true;
    }

    @Transactional
    public Boolean remove(Long roomId) {
        RoomEntity roomEntity = roomRepository.findById(roomId).orElseThrow(ResultCode.RECORD_NOT_EXISTS::toException);
        apartmentPermissionServer.checkPermission(roomEntity.getApartmentId());
        roomRepository.deleteById(roomId);

        // 删除附件
        attachmentRepository.deleteAllByBelongIdAndBelongType(roomId, 2);

        // 删除设备
        trRoomDeviceRepository.deleteAllByRoomId(roomId);
        return true;
    }

    public RoomVo details(Long id) {
        RoomEntity findResult = roomRepository.findById(id).orElseThrow(ResultCode.RECORD_NOT_EXISTS::toException);
        if (findResult == null) {
            throw ResultCode.RECORD_NOT_EXISTS.toException();
        }
        RoomVo vo = RoomMapstruct.INSTANCE.map(findResult);

        // 附件
        List<AttachmentEntity> attachmentEntityList = attachmentRepository.findAllByBelongIdAndBelongType(id, 2);
        attachmentEntityList.sort(Comparator.comparingInt(e -> e.getSerialNumber() == null ? Integer.MAX_VALUE : e.getSerialNumber()));
        vo.setAttachmentList(AttachmentMapstruct.INSTANCE.map(attachmentEntityList));

        // 设备
        List<TrRoomDeviceEntity> deviceList = trRoomDeviceRepository.findAllByRoomId(id);
        if (deviceList != null && !deviceList.isEmpty()) {
            List<Long> deviceIdList = deviceList.stream().map(TrRoomDeviceEntity::getDeviceId).collect(Collectors.toList());
            List<DeviceDictEntity> deviceDictList = deviceDictRepository.findAllById(deviceIdList);
            vo.setDeviceList(DeviceMapstruct.INSTANCE.map(deviceDictList));
        }
        return vo;
    }

    public PageVo<RoomVo> list(RoomListParams params) {
        Specification<RoomEntity> where = SpecBuilder.<RoomEntity>builder()
                .equal("apartmentId", params.getApartmentId())
                .equal("suitesId", params.getSuitesId())
                .equal("houseType", params.getHouseType())
                .buildAnd();

        Page<RoomEntity> pageResult = roomRepository.findAll(where, params.generatePageable());

        List<RoomEntity> records = pageResult.getContent();
        List<RoomVo> voList = RoomMapstruct.INSTANCE.mapAsList(records);

        return PageVo.of(pageResult, voList);
    }

    public List<RoomGroupByFloor> allGroupByFloor(RoomGroupByFloorParams params) {
        Long apartmentId = params.getApartmentId();

        List<RoomEntity> allByApartmentId = roomRepository.findAllByApartmentId(apartmentId);
        List<RoomVo> roomVoList = RoomMapstruct.INSTANCE.mapAsList(allByApartmentId);
        Map<String, List<RoomVo>> collect = roomVoList.stream().collect(Collectors.groupingBy(
                room -> room.getFloor() == null ? "其他" : room.getFloor() + "楼"));

        List<RoomGroupByFloor> result = new ArrayList<>();
        for (Map.Entry<String, List<RoomVo>> entry : collect.entrySet()) {
            String title = entry.getKey();
            List<RoomVo> floorRoomList = entry.getValue();
            result.add(new RoomGroupByFloor(floorRoomList.get(0).getFloor(), title, floorRoomList));
        }
        result.sort(Comparator.comparingInt(room -> ObjUtils.defaultIfNull(room.getFloor(), 0)));
        return result;

    }

    public TouristsViewRoomDetails touristsViewRoomDetails(Long id) {
        RoomEntity findResult = roomRepository.findById(id).orElseThrow(ResultCode.RECORD_NOT_EXISTS::toException);
        if (findResult == null) {
            throw ResultCode.RECORD_NOT_EXISTS.toException();
        }
        TouristsViewRoomDetails vo = RoomMapstruct.INSTANCE.mapToTouristsViewRoomDetails(findResult);

        // 附件
        List<AttachmentEntity> attachmentEntityList = attachmentRepository.findAllByBelongIdAndBelongType(id, 2);
        attachmentEntityList.sort(Comparator.comparingInt(e -> e.getSerialNumber() == null ? Integer.MAX_VALUE : e.getSerialNumber()));
        vo.setAttachmentList(AttachmentMapstruct.INSTANCE.map(attachmentEntityList));

        // 设备
        List<TrRoomDeviceEntity> deviceList = trRoomDeviceRepository.findAllByRoomId(id);
        if (deviceList != null && !deviceList.isEmpty()) {
            List<Long> deviceIdList = deviceList.stream().map(TrRoomDeviceEntity::getDeviceId).collect(Collectors.toList());
            List<DeviceDictEntity> deviceDictList = deviceDictRepository.findAllById(deviceIdList);
            vo.setDeviceList(DeviceMapstruct.INSTANCE.map(deviceDictList));
        }

        List<Long> butlerIdList = apartmentManagerRepository.findAllByApartmentIdAndIsButler(findResult.getApartmentId(), (byte) 1)
                .stream().map(ApartmentManager::getUserId).toList();
        List<UserEntity> butlerUser = userRepository.findAllById(butlerIdList);
        List<UserVo> userVos = UserMapstruct.INSTANCE.mapAsList(butlerUser);
        vo.setButlerList(userVos);

        // 从公寓信息获取地理位置
        ApartmentEntity apartmentEntity = apartmentMapper.selectById(findResult.getApartmentId());
        if (apartmentEntity != null) {
            vo.setLocationAddress(apartmentEntity.getLocationAddress());
            vo.setLocationName(apartmentEntity.getLocationName());
            vo.setLocationLatitude(apartmentEntity.getLocationLatitude());
            vo.setLocationLongitude(apartmentEntity.getLocationLongitude());
        }
        return vo;
    }
}
